
#include "Sim.h"

using namespace std;

/*----------------------------------------------------------------------------*/
Sim::Sim() {

	srand( (unsigned int)time(NULL) );
	G = (6+(2/3.0))*0.0001;

	userInit();

	timeNow = timePrev = clock();
	timePass = 0;
	timeDT = 1.0;
}

/*----------------------------------------------------------------------------*/
void Sim::userInit() {

	cout << "Number of Moons: ";
	cin >> numBodies;

	numBodies++; // Include the planet
	bodies = new GravBody[numBodies];

	int doRand;
	cout << "Generate Random? (0 or 1): ";
	cin >> doRand;

	bodies[0].init(vec3f(0,0,0), vec3f(0,0,0), 1, 1);

	for ( int i = 1 ; i < numBodies ; i++ ) {

		float mass;
		float dist;
		float rad;

		if ( doRand ) {
			float mRange = (rand()%100)*0.01f;
			float rRange = (rand()%100)*0.01f;
			mass = 0.001f+mRange*0.015f;
			dist = 12.0f+2*i;
			rad = 0.05f+rRange*0.35f;
		}
		else {
			cout << "Moon " << i << " - Radius (0 to 1) relative to planet: ";
			cin >> rad;
			cout << "Moon " << i << " - Mass (0 to 1) relative to planet: ";
			cin >> mass;
			cout << "Moon " << i << " - Diameters from planet: ";
			cin >> dist;
			dist *= bodies[0].getRad()*2;
		}

		vec3f loc = scaleVec( randVec(), dist );

		// Calculate +/- speed for circular orbit
		double thisG = G*1000; // Into milliseconds form
		float spd = (float)sqrt( G*(bodies[0].getMass()+mass) / dist );
		spd *= (rand()%2 ? 1 : -1);

		// Calculate angular velocity for circular orbit
		vec3f vel = cProd( subVec(*bodies[0].getLoc(),loc), vec3f(0,0,1) );
		vel = scaleVec(normalize(vel), spd);

		bodies[i].init(loc,vel,rad,mass);
	}
}

/*----------------------------------------------------------------------------*/
void Sim::start() {
	while ( true ) { update(); }
}

/*----------------------------------------------------------------------------*/
void Sim::update() {

	timePrev = timeNow;
	timeNow = clock();

	// Only update if the clock has changed
	if ( !(timePass = timeNow-timePrev) ) { return; }

	float timeStep = timePass*timeDT;
	double currG = G*timeStep;

	// Start at one so planet is not affected by moons
	for ( int a = 1 ; a < numBodies ; a++ ) {

		vec3f sumForces = vec3f(0,0,0);

		for ( int b = 0 ; b < numBodies ; b++ ) {

			if ( a == b ) { continue; }

			vec3f AB = subVec(*bodies[b].getLoc(), *bodies[a].getLoc());
			float distSqr = mag(AB);
			AB = normalize(AB);

			float massMult = bodies[a].getMass()*bodies[b].getMass();
			float coeff = (float)(currG*massMult / distSqr);
			vec3f force = scaleVec(AB, coeff);

			sumForces = addVec(sumForces, force);
		}

		// Make inertia/mass a factor (because force is in Newtons)
		sumForces = scaleVec(sumForces, 1/bodies[a].getMass());

		bodies[a].updateWithForce(sumForces, timeStep);
		bodies[a].updateHistory(timeStep);
	}
}

/*----------------------------------------------------------------------------*/
GravBody* Sim::getGravBody(int index) {
	return &bodies[index];
}

/*----------------------------------------------------------------------------*/
int Sim::getNumBodies() { 
	return numBodies; 
}
